﻿///////////////////////////////////////////////////////
//
// XDesigner.ORM 
//
// autohr : yuans
//
// date: 2011-2-23
//
// email  : yyf9989@hotmail.com
// 
//
///////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms ;
using XDesigner.ORM;
using XDesigner.ORM.DOM;
using System.Collections ;
using System.ComponentModel;
using System.Data;

namespace XDesigner.ORM
{
    [System.Drawing.ToolboxBitmap(typeof( ORMBindingSource ))]
    public class ORMBindingSource : BindingSource
    {
         public static ORMBindingSource CreatePackage( object parentInstance , Type instanceType , object datasource )
        {
            ORMBindingSource bs = new ORMBindingSource();
            bs.LogItemChange = false;
            if (parentInstance is ORMEngine)
            {
                bs._Engine = (ORMEngine)parentInstance;
                if (bs._Engine != null && instanceType != null)
                {
                    bs._Helper = bs._Engine.MappingHelpers[instanceType];
                    bs._ORMType = bs._Engine.Project.Types[instanceType];
                }
            }
            else
            {
                bs.ParentInstance = parentInstance;
                if (parentInstance != null && parentInstance is IORMInstance)
                {
                    IORMInstance oi = (IORMInstance)parentInstance;
                    bs._Engine = oi.ORMEngine;
                    if (bs._Engine != null && instanceType != null)
                    {
                        bs._Helper = bs._Engine.MappingHelpers[instanceType];
                        bs._ORMType = bs._Engine.Project.Types[instanceType];
                    }
                }
            }
            bs.DataSource = datasource;

            //foreach (object obj in items)
            //{
            //    bs.List.Add(obj);
            //    if (bs._Engine == null && obj is IORMInstance)
            //    {
            //        IORMInstance oi = (IORMInstance)obj;
            //        bs._Engine = oi.ORMEngine;
            //        if (bs._Engine != null)
            //        {
            //            bs._Helper = bs._Engine.MappingHelpers[instanceType];
            //            bs._ORMType = bs._Engine.Project.Types[instanceType];
            //        }
            //    }
            //}
            bs.LogItemChange = true;
            return bs;
        }

        public ORMBindingSource()
        {
        }

        public ORMBindingSource(ORMEngine work, Type type)
        {
            this._Engine = work;
            if (work != null)
            {
                this._ORMType = work.Project.Types[type];
                this._Helper = work.MappingHelpers[this._ORMType];
            }
        }

        public ORMBindingSource(
            ORMEngine work,
            Type type,
            object instances)
        {
            this._Engine = work;
            if (work != null)
            {
                this._ORMType = work.Project.Types[type];
                this._Helper = work.MappingHelpers[this._ORMType];
            }
            if (instances != null)
            {
                Type t = instances.GetType();
                if (t.IsArray)
                {
                    foreach (object obj in ((System.Collections.IEnumerable)instances))
                    {
                        this.List.Add(obj);
                    }
                }
                else
                {
                    this.DataSource = instances;
                }
            }
        }

        private object _ParentInstance = null;

        public object ParentInstance
        {
            get
            {
                return _ParentInstance; 
            }
            set
            {
                _ParentInstance = value; 
            }
        }


        private ORMEngine _Engine = null;

        public ORMEngine Engine
        {
            get
            {
                return _Engine;
            }
            set
            {
                _Engine = value;
            }
        }
        private ORMTypeInfo _ORMType = null;

        public ORMTypeInfo ORMType
        {
            get
            { 
                return _ORMType; 
            }
            set
            {
                _ORMType = value;
            }
        }

        private ORMTypeMappingHelper _Helper = null;

        public ORMTypeMappingHelper Helper
        {
            get
            { 
                return _Helper;
            }
            set
            {
                _Helper = value;
            }
        }

        private void CheckORMType(bool checkEngine)
        {
            if (_ORMType == null)
            {
                throw new InvalidOperationException("ORMType is null");
            }
            if (checkEngine)
            {
                if (_Engine == null)
                {
                    throw new InvalidOperationException("Engine is null");
                }
            }
        }

        private bool _LogItemChange = false;
        /// <summary>
        /// can log item change task.
        /// </summary>
        public bool LogItemChange
        {
            get
            { 
                return _LogItemChange;
            }
            set
            { 
                _LogItemChange = value; 
            }
        }

        public override int Add(object value)
        {
            CheckORMType(false);
            this.ORMType.CheckInstanceType(value, true);
            return base.Add(value);
        }

        public void AddRange(System.Collections.IEnumerable items)
        {
            CheckORMType(false);
            foreach (object item in items)
            {
                this.ORMType.CheckInstanceType(item, true);
                base.Add(item);
            }
        }

        protected override void OnAddingNew(System.ComponentModel.AddingNewEventArgs e)
        {
            if (e.NewObject == null)
            {
                CheckORMType(true);
                object instance = this.Engine.CreateInstance(this.ORMType);
                if (instance != null && this.ParentInstance != null && this._Engine != null )
                {
                    foreach (ORMPropertyInfo p in this.ORMType.Properties)
                    {
                        if (p.ParentReference == ORMBooleanValue.True)
                        {
                            if (this.ParentInstance.GetType().Equals(p.PropertyType)
                                || this.ParentInstance.GetType().IsSubclassOf(p.PropertyType))
                            {
                                ORMTypeMappingHelper helper2 = this._Engine.MappingHelpers[instance.GetType()];
                                if (helper2 != null)
                                {
                                    helper2.SetPropertyValue(
                                        instance,
                                        p.Name,
                                        this.ParentInstance);
                                    //break;
                                }
                            }//if
                        }//if
                    }//foreach
                }
                e.NewObject = instance;
            }//if
            base.OnAddingNew(e);
        }

        public override void ApplySort(ListSortDescriptionCollection sorts)
        {
            // sort items by bubble.
            int itemCount = this.Count;

            for (int iCount1 = 0; iCount1 < itemCount; iCount1++)
            {
                for (int iCount2 = itemCount - 1; iCount2 > iCount1; iCount2--)
                {
                    object item1 = this[iCount2 - 1];
                    object item2 = this[iCount2];
                    foreach (ListSortDescription sort in sorts)
                    {
                        int result = CompareItem(
                            item1, 
                            item2, 
                            sort.PropertyDescriptor,
                            sort.SortDirection);
                        if (result > 0)
                        {
                            this[iCount1] = item2;
                            this[iCount2] = item1;
                            break;
                        }
                        else if (result < 0)
                        {
                            break;
                        }
                    }
                }//for
            }//for
        }

        public override void ApplySort(PropertyDescriptor property, ListSortDirection sort)
        {
            // sort items by bubble.
            int itemCount = this.Count;
            for (int iCount1 = 0; iCount1 < itemCount; iCount1++)
            {
                for( int iCount2 = itemCount -1 ; iCount2 > iCount1 ; iCount2 -- )
                {
                    object item1 = this[iCount2 - 1];
                    object item2 = this[iCount2];
                    int result = CompareItem(item1, item2, property, sort);
                    if (result > 0)
                    {
                        this[iCount1] = item2;
                        this[iCount2] = item1;
                    }
                }//for
            }//for
        }


        protected virtual int CompareItem(
            object item1,
            object item2 ,
            PropertyDescriptor property,
            ListSortDirection sort)
        {
            object v1 = null;
            object v2 = null;
            if ( this._Helper == null)
            {
                v1 = property.GetValue(item1);
                v2 = property.GetValue(item2);
            }
            else
            {
                v1 = this._Helper.GetPropertyValue(item1, property.Name);
                v2 = this._Helper.GetPropertyValue(item2, property.Name);
            }
            int result = 0;
            if (v1 is System.IComparable)
            {
                result = ((IComparable)v1).CompareTo(v2);
            }
            if (sort == ListSortDirection.Descending)
            {
                result = -result;
            }
            return result;
        }

        //private Hashtable myRecordState = new Hashtable();

        public event EventHandler Changed = null;

        [System.ComponentModel.Browsable( false )]
        public bool Modified
        {
            get
            {
                if (_RemovedInstances != null && _RemovedInstances.Count > 0)
                {
                    return true;
                }
                foreach (object obj in this)
                {
                    IORMInstance oi = obj as IORMInstance;
                    if (oi != null && oi.ORMDataState != DataRowState.Unchanged )
                    {
                        return true;
                    }
                }
                return false;
            }
        }


        private bool _EnableCheckedProperty = false;

        public bool EnableCheckedProperty
        {
            get
            {
                return _EnableCheckedProperty;
            }
            set
            {
                _EnableCheckedProperty = value;
            }
        }

        private class CheckedPropertyDescriptor : PropertyDescriptor
        {

            public CheckedPropertyDescriptor(ORMBindingSource bs )
                : base("InstanceChecked" , null )
            {
                _BindingSource = bs;
            }

            private ORMBindingSource _BindingSource = null;

            public override string Name
            {
                get
                {
                    return "InstanceChecked";
                }
            }
            public override string Category
            {
                get
                {
                    return "Data";
                }
            }
            public override bool CanResetValue(object component)
            {
                return true;
            }

            public override Type ComponentType
            {
                get
                {
                    if (this._BindingSource == null)
                        return typeof(object);
                    else
                        return this._BindingSource.ORMType.InstanceType;
                }
            }

            public override object GetValue(object component)
            {
                if (this._BindingSource != null)
                {
                    return this._BindingSource._CheckedInstances.Contains(component);
                }
                return false;
            }

            public override bool IsReadOnly
            {
                get { return false; }
            }

            public override Type PropertyType
            {
                get { return typeof(bool); }
            }

            public override void ResetValue(object component)
            {
                SetValue(component, false);
            }

            public override void SetValue(object component, object value)
            {
                if (this._BindingSource != null)
                {
                    bool bol = Convert.ToBoolean(value);
                    if (this._BindingSource._CheckedInstances.Contains(component) != bol)
                    {
                        if (bol)
                        {
                            this._BindingSource._CheckedInstances.Add(component);
                        }
                        else
                        {
                            this._BindingSource._CheckedInstances.Remove(component);
                        }
                    }
                }
            }

            public override bool ShouldSerializeValue(object component)
            {
                return false;
            }
        }

        private System.Collections.ArrayList _CheckedInstances = new ArrayList();

        public System.Collections.ArrayList CheckedInstances
        {
            get { return _CheckedInstances; }
        }

        public override PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
        {
            if (this.EnableCheckedProperty)
            {
                ArrayList list = new ArrayList();
                if (this.ORMType == null)
                {
                    list.AddRange(listAccessors);
                }
                else
                {
                    list.AddRange(TypeDescriptor.GetProperties(this.ORMType.InstanceType));
                }
                list.Add(new CheckedPropertyDescriptor(this));
                return new PropertyDescriptorCollection((PropertyDescriptor[])list.ToArray(typeof(PropertyDescriptor)));

            }
            else
            {
                if (this.ORMType != null)
                {
                    return TypeDescriptor.GetProperties(this.ORMType.InstanceType);
                }
                else
                {
                    return new PropertyDescriptorCollection( listAccessors );
                }
            }
        }

        //public class ListPropertyDescriptor : PropertyDescriptor 
        //{
        //    public ListPropertyDescriptor()
        //    {
        //    }

        //    public ListPropertyDescriptor(PropertyDescriptor baseP)
        //    {
        //        baseDescriptor = baseP;
        //    }
        //    private PropertyDescriptor baseDescriptor = null;

        //    public override bool ShouldSerializeValue(object component)
        //    {
        //        return baseDescriptor.ShouldSerializeValue( component );
        //    }
        //    public override void AddValueChanged(object component, EventHandler handler)
        //    {
        //        baseDescriptor.AddValueChanged(component, handler);
        //    }

        //    public override AttributeCollection Attributes
        //    {
        //        get
        //        {
        //            return baseDescriptor.Attributes;
        //        }
        //    }

        //    public override bool CanResetValue(object component)
        //    {
        //        return baseDescriptor.CanResetValue(component);
        //    }

        //    public override string Category
        //    {
        //        get
        //        {
        //            return baseDescriptor.Category;
        //        }
        //    }

        //    public override Type ComponentType
        //    {
        //        get
        //        {
        //            return baseDescriptor.ComponentType;
        //        }
        //    }

        //    public override TypeConverter Converter
        //    {
        //        get
        //        {
        //            return baseDescriptor.Converter;
        //        }
        //    }

        //    public override string Description
        //    {
        //        get
        //        {
        //            return baseDescriptor.Description;
        //        }
        //    }

        //    public override bool IsBrowsable
        //    {
        //        get
        //        {
        //            return baseDescriptor.IsBrowsable;
        //        }
        //    }

        //    public override bool IsReadOnly
        //    {
        //        get { return baseDescriptor.IsReadOnly; }
        //    }

        //    public override string Name
        //    {
        //        get
        //        {
        //            return baseDescriptor.Name;
        //        }
        //    }

        //    public override string DisplayName
        //    {
        //        get
        //        {
        //            return baseDescriptor.DisplayName;
        //        }
        //    }

        //    public override object GetEditor(Type editorBaseType)
        //    {
        //        return baseDescriptor.GetEditor(editorBaseType);
        //    }

        //    public override bool DesignTimeOnly
        //    {
        //        get
        //        {
        //            return baseDescriptor.DesignTimeOnly;
        //        }
        //    }

        //    public override object GetValue(object component)
        //    {
        //        return baseDescriptor.GetValue(component);
        //    }

        //    public override void SetValue(object component, object value)
        //    {
        //        baseDescriptor.SetValue(component, value);
        //    }

        //    public override Type PropertyType
        //    {
        //        get { return typeof(  ListContainer ) ;}
        //    }

        //    public override void ResetValue(object component)
        //    {
        //        baseDescriptor.ResetValue(component);
        //    }

        //    public override string ToString()
        //    {
        //        return baseDescriptor.ToString();
        //    }

        //    public override bool IsLocalizable
        //    {
        //        get
        //        {
        //            return baseDescriptor.IsLocalizable;
        //        }
        //    }


        //}



        //public class ListContainer
        //{
        //    public ListContainer(IList lst)
        //    {
        //        _Value = lst;
        //    }

        //    private IList _Value = null;

        //    public IList Value
        //    {
        //        get { return _Value; }
        //        set { _Value = value; }
        //    }
        //}
        //public bool SetRecordState(object instance, DataRowState state)
        //{
        //    if (this.Contains(instance))
        //    {
        //        if (myRecordStates.ContainsKey(instance)
        //            && myRecordStates[instance] == DataRowState.Added)
        //        {
        //            if (this.Changed != null)
        //            {
        //                this.Changed(this, EventArgs.Empty);
        //            }
        //        }
        //        else
        //        {
        //            myRecordStates[instance] = state;
        //            if (this.Changed != null)
        //            {
        //                this.Changed(this, EventArgs.Empty);
        //            }
        //        }
        //        return true;
        //    }
        //    else
        //    {
        //        return false;
        //    }
        //}

        public void AddOuterReference()
        {
            if (this.Engine != null)
            {
                foreach (object obj in this)
                {
                    this.Engine.Buffer.AddOuterReference(obj, this, null);
                }
            }
        }

        private ArrayList _RemovedInstances = null;

        protected override void OnBindingComplete(BindingCompleteEventArgs e)
        {
            base.OnBindingComplete(e);
            this.LogItemChange = true;
        }

        public override void Remove(object value)
        {
            base.Remove(value);
            if (this.LogItemChange)
            {
                LogRemoveInstance(value);
            }
        }

        public override void RemoveAt(int index)
        {
            if (this.LogItemChange)
            {
                int iCount = this.Count;
                object instance = this.List[index];
                base.RemoveAt(index);
                LogRemoveInstance(instance);
            }
            else
            {
                base.RemoveAt(index);
            }
        }

        private void LogRemoveInstance(object instance)
        {
            if (_RemovedInstances == null)
            {
                _RemovedInstances = new ArrayList();
            }
            if (this.Engine != null && this.Engine.Config.UpdateImmediately)
            {
                this.Engine.Delete(instance);
            }
            else
            {
                IORMInstance oi = instance as IORMInstance;
                if (oi != null)
                {
                    if (oi.ORMDataState == DataRowState.Unchanged
                        || oi.ORMDataState == DataRowState.Modified)
                    {
                        oi.ORMDataState = DataRowState.Deleted;
                        _RemovedInstances.Add(oi);
                    }
                }
            }
            if (this.Changed != null)
            {
                this.Changed(this, EventArgs.Empty);
            }
        }

        
        protected override void OnListChanged( ListChangedEventArgs e)
        {
            if (this.LogItemChange)
            {
                switch (e.ListChangedType)
                {
                    case ListChangedType.ItemAdded:
                        {
                            IORMInstance oi = this.List[e.NewIndex] as IORMInstance;
                            if (oi != null)
                            {
                                oi.ORMDataState = DataRowState.Added;
                            }
                            if (this.Changed != null)
                            {
                                this.Changed(this, EventArgs.Empty);
                            }
                        }
                        break;
                    case ListChangedType.ItemChanged:
                        {
                            IORMInstance oi = this.List[e.NewIndex] as IORMInstance;
                            if (oi != null)
                            {
                                if (oi.ORMDataState == DataRowState.Unchanged)
                                {
                                    oi.ORMDataState = DataRowState.Modified;
                                }
                            }
                            if (this.Changed != null)
                            {
                                this.Changed(this, EventArgs.Empty);
                            }
                        }
                        break;
                }
            }
            base.OnListChanged(e);
        }
         

        /// <summary>
        /// Update data to database
        /// </summary>
        /// <returns>number of database records effect</returns>
        public int Update()
        {
            int result = 0;
            if (this.Modified)
            {
                this.Engine.OpenConnection();
                foreach (object instance in this)
                {
                    IORMInstance oi = instance as IORMInstance;
                    if (oi != null)
                    {
                        if (oi.ORMDataState == DataRowState.Added)
                        {
                            int r = this.Engine.Insert(oi);
                            if (r > 0)
                            {
                                oi.ORMDataState = DataRowState.Unchanged;
                                result += r;
                            }
                        }
                        else if (oi.ORMDataState == DataRowState.Modified)
                        {
                            int r = this.Engine.Update(oi);
                            if (r > 0)
                            {
                                oi.ORMDataState = DataRowState.Modified;
                                result += r;
                            }
                        }
                    }//if
                }//foreach
                if (this._RemovedInstances != null)
                {
                    for (int iCount = this._RemovedInstances.Count - 1; iCount >= 0; iCount--)
                    {
                        result += this.Engine.Delete(this._RemovedInstances[iCount]);
                        this._RemovedInstances.RemoveAt(iCount);
                    }
                    this._RemovedInstances = null;
                }
            }
            return result;
        }//public int Update( )

        protected override void Dispose(bool disposing)
        {
            if (this.Changed != null )
            {
                this.Changed = null;
            }
            base.Dispose(disposing);
        }
    }
}
